# badbot.py - example of how to use the API and libraries.
#   Currently the code behind ai_PuzzleBot on weewar.com

import random
from weebotlib import *           # Core API library.
from weegame import *             # Map state management built on API info.
from weewarbot import WeewarBot   # Shell class for the bot used here


class Badbot(WeewarBot):
  ''' Our strategy is pretty simple for this bot.
  '''
  def unitTurn(self, game, unit):
    print "Taking a turn with in game %d with:"%game.id, unit.x, unit.y, unit.type
    if unit.finished=="true":
      print "Unit finished."
      return
    moved = False
    def isCapturable(target):
      if target and target.__dict__.has_key("terraintype"):
        if target.terraintype in ["Base", "Harbor", "Airfield"]:
          if target.owner != self.username:
            return True
      return False
    def canCapture(target):
      if isCapturable(target):
        if unit.type in ["Trooper", "Heavy Trooper", "Hovercraft"]:
          return True
      return False
    def canAttack(target):
      victims = self.game_api.attackOptions(game.id, int(target.x), int(target.y), unit.type)
      if victims.__dict__.has_key("coordinate"):
        return victims.coordinate[0]
      else:
        return False

    def findMove(x, y, mapstate, choices):
      if random.randint(0,100) == 4:
        print "random move"
        return random.choice(choices)
      # loop through map to find a base
      mindist = 9999
      mindistbase = None
      bestmove = None
      bases = {}
      distances = []
      mindistbase, mindist = findNearest(x, y, mapstate, isCapturable)
      if mindistbase:
        mx = mindistbase.x
        my = mindistbase.y
        for j in choices:
          dist = distance(mx,my,j.x,j.y)
          if dist <= mindist:
            mindist = dist
            bestmove = j
        if bestmove:
          print bestmove.x,bestmove.y, "best move towards:", mx, my
        return bestmove if bestmove else random.choice(choices)
      else:
        # return a random movement I guess?
        return random.choice(choices)

    movechoices = self.game_api.moveOptions(game.id, int(unit.x), int(unit.y), unit.type)
    choices = []
    # make a movement choice list
    if "coordinate" in movechoices.__dict__:
      for choice in movechoices.coordinate:
        target = game.mapstate.get(choice.x, choice.y)
        if target.unitinfo is None:
          choices.append(target)
    if choices:
      # loop through possible moves for each unit (including no movement)
      for choice in choices:
        # try to cap if move spot is an non-owned base and unit is a trooper:
        target = choice
        if canCapture(target):
          cmd = self.game_api.createUnitCommand(game.id, int(unit.x), int(unit.y))
          cmd = self.game_api.moveUnit(cmd, int(target.x), int(target.y))
          cmd = self.game_api.captureUnit(cmd)
          value = self.game_api.sendUnitCommand(cmd)
          if "error" in str(value[0]):
            continue
          else:
            return
    # check the spot they're standing on
    target = game.mapstate.get(unit.x, unit.y)
    if canCapture(target):
      cmd = self.game_api.createUnitCommand(game.id, int(unit.x), int(unit.y))
      cmd = self.game_api.captureUnit(cmd)
      self.game_api.sendUnitCommand(cmd)
      return
    if choices:
      for choice in choices:
        # if move spot can attack, do that
        target = choice
        victim = canAttack(target)
        if victim:
          cmd = self.game_api.createUnitCommand(game.id, int(unit.x), int(unit.y))
          cmd = self.game_api.moveUnit(cmd, int(target.x), int(target.y))
          cmd = self.game_api.attackUnit(cmd, int(victim.x), int(victim.y))
          value = self.game_api.sendUnitCommand(cmd)
          if "error" in str(value[0]):
            continue
          else:
            return
    target = game.mapstate.get(unit.x, unit.y)
    victim = canAttack(target)
    if victim:
      cmd = self.game_api.createUnitCommand(game.id, int(unit.x), int(unit.y))
      cmd = self.game_api.attackUnit(cmd, int(victim.x), int(victim.y))
      self.game_api.sendUnitCommand(cmd)
      return
    # if hurt and not on a base:
      # repair
    # pick a not-so-random move
    if choices:
      choice = findMove(int(unit.x), int(unit.y), game.mapstate, choices)
      #choice = random.choice(choices.coordinate)
      cmd = self.game_api.createUnitCommand(game.id, int(unit.x), int(unit.y))
      cmd = self.game_api.moveUnit(cmd, int(choice.x), int(choice.y))
      value = self.game_api.sendUnitCommand(cmd)
    else:
      # try to repair otherwise
      cmd = self.game_api.createUnitCommand(game.id, int(unit.x), int(unit.y))
      cmd = self.game_api.repairUnit(cmd)
      value = self.game_api.sendUnitCommand(cmd)

  def baseTurn(self, game, base):
    # build troopers
    print "Taking a turn in game %d with:"%game.id, base.x, base.y, base.type
    def build(name):
      self.game_api.buildUnit(game.id, int(base.x), int(base.y), name)
    def isEnemy(cell):
      if cell and cell.unitinfo and cell.unitinfo.owner != self.username:
        return True
      else:
        return False
    enemycell, dist = findNearest(int(base.x), int(base.y), game.mapstate, isEnemy)
    if base.type=="Base":
      # build a heavy unit if we're under attack
      if enemycell and dist < 3:
        print "enemy nearby is", enemycell.unitinfo.type, "at", dist
        if enemycell.unitinfo.type in ["Helicopter","Bomber","Jetfighter"]:
          build("Heavy Trooper")
        build("Heavy Tank")
        build("Hovercraft")
        build("Heavy Trooper")
        build("Trooper")
      else:
        water = game.mapstate.terraincount["Water"]
        swamp = game.mapstate.terraincount["Swamp"]
        desert = game.mapstate.terraincount["Desert"]
        forest = game.mapstate.terraincount["Forest"]
        mountain = game.mapstate.terraincount["Mountain"]
        total = game.mapstate.terraincount["total"]
        hcraftbias = float(water+swamp+(desert*0.5)-((forest+mountain)*2.0)/float(total))
        if hcraftbias > 25:
          hcraftbias = 25
        if random.random()<=hcraftbias:
          build("Hovercraft")
        build("Trooper")
    if base.type=="Harbor":
      if enemycell and dist < 8:
        build("Destroyer")
        build("Hovercraft")
      else:
        build("Hovercraft")
    if base.type=="Airfield":
      build("Jetfighter")

  def playCityTurn(self, game):
    buildUnits = True
    mapstate = game.mapstate

    if game.round == 1:
      # move trooper to 21,5
      unit = mapstate.get(19,4).unitinfo
      if unit and unit.owner==self.username:
        cmd = self.game_api.createUnitCommand(game.id, int(unit.x), int(unit.y))
        cmd = self.game_api.moveUnit(cmd, 21, 5)
        self.game_api.sendUnitCommand(cmd)
    if game.round == 2:
      # move trooper to 22,7
      unit = mapstate.get(21,5).unitinfo
      if unit and unit.owner==self.username:
        cmd = self.game_api.createUnitCommand(game.id, int(unit.x), int(unit.y))
        cmd = self.game_api.moveUnit(cmd, 22, 7)
        self.game_api.sendUnitCommand(cmd)
        buildUnits = False
      pass
    if game.round == 3:
      # move trooper to 23,8
      # capture 23.8
      unit = mapstate.get(22,7).unitinfo
      if unit and unit.owner==self.username:
        cmd = self.game_api.createUnitCommand(game.id, int(unit.x), int(unit.y))
        cmd = self.game_api.moveUnit(cmd, 23, 8)
        cmd = self.game_api.captureUnit(cmd)
        self.game_api.sendUnitCommand(cmd)
        buildUnits = False
    if game.round == 4:
        buildUnits = False
    if game.round ==5:
      if mapstate.get(23,8).owner==self.username:
        self.game_api.buildUnit(game.id, 23, 8, "Submarine")

    myunits = game.getPlayerUnits(self.username)
    for unit in myunits:
      if unit.type != "Submarine":
        self.unitTurn(game, unit)
    # loop through my bases
    if buildUnits: #or there's a submarine on 23,8
      for base in game.getPlayerTerritory(self.username):
        self.baseTurn(game, base)
    self.game_api.finishTurn(game.id)

  def playTurn(self, gameid):
      # accept invitation
      # TODO: detect if we need to accept.
      self.game_api.acceptInvitation(gameid)
      # get the map state
      mapinfo = self.site_api.getGameState(gameid)
      game = WeewarGame(mapinfo)
      if game.rated == "true":
        # we don't play rated games!
        print "We don't play rated games. gameid:", gameid
        self.game_api.abandon(gameid)
        self.game_api.surrender(gameid)
        # TODO: Tell them when they add chat API
        return

      print "getCurrentPlayer is: %s" % game.getCurrentPlayer()
      if self.username == game.getCurrentPlayer():
        print "We're current player!"

        # *******
        # NOTE: We have a special strategy on the City map!
        # *******
        if game.mapid==28127:
          self.playCityTurn(game)
          return

        # loop through my units
        units = list(game.getPlayerUnits(self.username))
        random.shuffle(units)
        for unit in units:
          self.unitTurn(game, unit)
        # loop through my bases
        for base in game.getPlayerTerritory(self.username):
          self.baseTurn(game, base)
        self.game_api.finishTurn(gameid)

  def getInfo(self):
    # get my state
    self.info = self.site_api.getHeadquartersInfo()
    return self.info

  def playTurns(self):
    ''' Not used if you use run_badbot.py '''
    info = self.getInfo()
    # loop through my games
    for mygame in info.game:
      gameid = int(mygame.id.PCDATA)
      if mygame.__dict__.has_key("inNeedOfAttention") and mygame.inNeedOfAttention == u"true":
        print "Playing turn: %d" %gameid
        self.playTurn(gameid)
    print "---------- Done with turns ------------"

